package algorytm;

import util.LagrInterpolation;
import util.MyPoint;
import util.ProgramConst;

/**
 * Algorytm oblicza nowe wartosci zmiennych, w zaleznosci od tego CO SIE ZMEINILO
 * Wiadomo, ze to co zostalo zmienione nie moze byc wyliczane na nowa z randoma
 *
 * @author Kamil Gajowy
 */
public final class Algorytm {

    public static final int ZmianaWolumenu          =   0;
    public static final int ZmianaJakosci           =   1;
    public static final int ZmianaCeny              =   2;
    public static final int ZmianaReklamyTV         =   3;
    public static final int ZmianaReklamyINET       =   4;
    public static final int ZmianaReklamyMag        =   5;
    public static final int ZmianaZYSKU             =   6;
    public static final int ZmianaRYZYKA            =   7;
    public static final int ZmianaKredytu            =   8;

    public static final double defaultRISK          = 0.75;

    public static final int BudzetReklamy           = 90000;

    MyPoint aktualnyPunkt;
    double ryzyko;
    double gotowka = ProgramConst.iloscGotowki;
    int wolumen;
    int runda = 1;
    int zysk;

    double rataKredytu;
    double poczatkowyKredyt = 0;

    double testKredyt = 0;

     LagrInterpolation cennik;


    /**
    * Default constructor.
     * @param daneStartowe
     */
    public Algorytm(MyPoint daneStartowe){
        cennik = new LagrInterpolation(0, 0);
        this.aktualnyPunkt = daneStartowe;
        this.ryzyko = Algorytm.defaultRISK;
        daneStartowe.setRyzyko(this.ryzyko);
        calculate(daneStartowe, Algorytm.ZmianaRYZYKA);
    }

    /**
     * Algorytm patrzy co sie zmienilo, i na podstawie tej konkretnej danej
     * wylicza reszte. Np jezeli zmienilismy wolumen, zmieni sie w glownej
     * mierze zysk, w mniejszym jakosc/reklama/cena, a w najmniejszym RYZYKO
     *
     * //tak roboczo, mam nadizeje ze dobrze mysle :D
     *
     * @param nowyPunkt
     * @param typZmiany
     */
    public void calculate(MyPoint nowyPunkt, int typZmiany){
        // myPoint + typ => aktualnyPunkt
        //if ( nowyPunkt.compareTo(aktualnyPunkt) == 0 ){
        if ( false ){
            System.out.println("_______________________________________________________brak zmian");
            return ;
        } else {
            //System.out.println("_______________________________________________________"+typZmiany);
            //calculate again;
            switch(typZmiany) {
                case Algorytm.ZmianaRYZYKA:
                    zmianaRyzyka(nowyPunkt);
                    break;
                case Algorytm.ZmianaCeny:
                    zmianaCeny(nowyPunkt);
                    break;
                case Algorytm.ZmianaJakosci:
                    zmianaJakosci(nowyPunkt);
                    break;
                case Algorytm.ZmianaWolumenu:
                    zmianaWolumenu(nowyPunkt);
                    break;
                case Algorytm.ZmianaKredytu:
                    zmianaKredytu(nowyPunkt);
                    break;
                case Algorytm.ZmianaReklamyINET:
                     zmianaReklamy(nowyPunkt,Algorytm.ZmianaReklamyINET);
                     break;
                case Algorytm.ZmianaReklamyMag:
                     zmianaReklamy(nowyPunkt,Algorytm.ZmianaReklamyMag);
                     break;
                case Algorytm.ZmianaReklamyTV:
                     zmianaReklamy(nowyPunkt,Algorytm.ZmianaReklamyTV);
                     break;
            }

        }
    }

    public MyPoint getCurrentData(){
        return this.aktualnyPunkt;
    }

    private void zmianaRyzyka(MyPoint nowyPunkt) {
        this.ryzyko = nowyPunkt.getRyzyko();
        aktualnyPunkt.setRyzyko(this.ryzyko);

        if ( runda == 1 ){
        aktualnyPunkt.setWzietyKredyt(7*ryzyko/8*ProgramConst.maxKredyt+
                ProgramConst.maxKredyt/8);
        }

        ryzyko *= 100;

        //int jakosc = (int)(100/Math.atan(ryzyko/5+1.5)) + 5;
        int jakosc = (int)((100/Math.atan(ryzyko/5+1.5))-0.12*ryzyko);
        aktualnyPunkt.setJakosc(jakosc);
        //System.out.println("~~~~~~~~~~~~~~~~~ jakosc " + aktualnyPunkt.getJakosc());

        /************ REKLAMY ***********/
//System.out.println("{{{{{{{{{ gotowka " + gotowka + "}}}}}}}}}}");
        double tempGotowka = gotowka -
                (ProgramConst.amortyzacja + ProgramConst.kosztyStale) -
                prognozujRateKredytu();

        if ( runda == 1 ){
            tempGotowka += aktualnyPunkt.getWzietyKredyt();
        }

//System.out.println("{{{{{{{{{ gotowkatemp " + tempGotowka + "}}}}}}}}}}");
        //System.out.println("RYZYKO = " + ryzyko);

        double procentNaReklame = -0.0005*ryzyko/100+0.05;
        double funduszNaReklame = tempGotowka * procentNaReklame;

        if ( funduszNaReklame >= Algorytm.BudzetReklamy ){
            funduszNaReklame = Algorytm.BudzetReklamy;
        }

        //System.out.println("~~~~~~~~~~~~~~~~~ fundusz na reklame " + funduszNaReklame);
        //System.out.println("~~~~~~~~~~~~~~~~~ procent% na reklame " + procentNaReklame);

        aktualnyPunkt.setReklamaInet(funduszNaReklame*2/10);
        aktualnyPunkt.setReklamaTV(funduszNaReklame*3/4);
        aktualnyPunkt.setReklamaMag(funduszNaReklame*5/100);

        tempGotowka -= (aktualnyPunkt.getReklamaInet() +
                aktualnyPunkt.getReklamaMag() + aktualnyPunkt.getReklamaTV());

        /************ CENA ***********/
        /********** WOLUMEN ***********/
        double cenaJed = nowyPunkt.getCenaJed();
        if (cenaJed == 0 ){
            cenaJed = cennik.getKosztJed(0, jakosc);
        }

        double newPrice = cenaJed * ( 1 + Math.log(ryzyko-0.8)) / 3.2;
        int ilosc = (int)(Math.floor(tempGotowka/newPrice));
        if ( ilosc < 0 ) {
            ilosc = 0;
        }

        //System.out.println(" &&&& PRODUKCJA " + ilosc + "/" + ProgramConst.zdlonoscProdukcyjna);
        if ( ilosc >= ProgramConst.zdlonoscProdukcyjna ){
            ilosc = ProgramConst.zdlonoscProdukcyjna;
        }

        //System.out.println("\t cenaJed\t " + cenaJed + "\tilosc " + ilosc);

        System.out.println("\t\t&&& ZASOBY TO " + tempGotowka + "\t&&&&&&&&&&");

        int totalInc = ilosc;

        boolean maxProd = false;
        while ( (tempGotowka - cenaJed*ilosc > 500  /*zapas*/
                || tempGotowka - cenaJed*ilosc < 0 )
                && !maxProd
                //|| ilosc >= ProgramConst.zdlonoscProdukcyjna
               // || ilosc < 1
                ) {

            if( tempGotowka - cenaJed*ilosc > 500 ){
                ++ilosc;
                ++totalInc;
                //sprawdzenie zeby sie nie zapetlilo miedzy 2 wartosciami

                if ( totalInc - ilosc > 40 ) {
                    --ilosc;
                    break;
                }

                //System.out.println("\twiecej ! ROZNICA = " + ((tempGotowka)-(cenaJed*ilosc)) );
            } else if (tempGotowka - cenaJed*ilosc < 0) {
                --ilosc;
                //System.out.println("\tmniej ! ROZNICA = " + ((tempGotowka)-(cenaJed*ilosc)) );
            } else {
                break;
            }



            //System.out.println(" &&&& PRODUKCJA " + ilosc + "/" + ProgramConst.zdlonoscProdukcyjna);
            if ( ilosc >= ProgramConst.zdlonoscProdukcyjna ){
                ilosc = ProgramConst.zdlonoscProdukcyjna;
                maxProd = true;
            }

            cenaJed = cennik.getKosztJed(ilosc, jakosc);

            newPrice = cenaJed * ( 2 + Math.log(ryzyko-0.8)) / 3.2;
            if ( newPrice < cenaJed ) {
                newPrice = cenaJed + 1;
            }
            //ilosc = (int)(Math.floor(tempGotowka/newPrice));
            //if ( ilosc < 0 ) {
            //    ilosc = 0;
            //}
            //System.out.println("@\t cenaJed\t " + cenaJed + "\tilosc " + ilosc + "\troznica w gotowce " + (tempGotowka - cenaJed*ilosc) );

        }

        aktualnyPunkt.setCenaJed(cenaJed);
        aktualnyPunkt.setCenaJedSprzedaz(newPrice);
        aktualnyPunkt.setWolumen(ilosc);

        System.out.println("~~~~~~~~~~~~~~~~~ cena jednostkowa " + cenaJed + " \t cena sprzedazy " + aktualnyPunkt.getCenaJedSprzedaz());
        System.out.println("~~~~~~~~~~~~~~~~~ ilosc " + aktualnyPunkt.getWolumen());

        ryzyko /= 100;
        
    }

    private void zmianaCeny(MyPoint nowyPunkt) {
        this.aktualnyPunkt.setCenaJed(nowyPunkt.getCenaJed());
        this.aktualnyPunkt.setCenaJedSprzedaz(nowyPunkt.getCenaJedSprzedaz());
    }

    private void zmianaJakosci(MyPoint nowyPunkt) {
        this.aktualnyPunkt.setJakosc(nowyPunkt.getJakosc());
    }

    private void zmianaWolumenu(MyPoint nowyPunkt) {
        this.aktualnyPunkt.setWolumen(nowyPunkt.getWolumen());
        this.aktualnyPunkt.setCenaJed(nowyPunkt.getCenaJed());

    }

    private void zmianaReklamy(MyPoint nowyPunkt, int ZmianaReklamyINET) {
        this.aktualnyPunkt.setReklamaInet(nowyPunkt.getReklamaInet());
        this.aktualnyPunkt.setReklamaMag(nowyPunkt.getReklamaMag());
        this.aktualnyPunkt.setReklamaTV(nowyPunkt.getReklamaTV());
    }

    private void zmianaKredytu(MyPoint nowyPunkt) {
        this.aktualnyPunkt.setWzietyKredyt(nowyPunkt.getWzietyKredyt());
        
    }

    private void poprawZysk(){
        
    }

    private double prognozujRateKredytu() {
        if ( aktualnyPunkt.getWzietyKredyt() == 0 ) return 0;
        return (aktualnyPunkt.getWzietyKredyt() / (6-runda) ) + aktualnyPunkt.getWzietyKredyt() * ProgramConst.oprocentowanieKredytu;
    }

    private void obliczDane() {
        if ( runda == 1 ) {
            gotowka = ProgramConst.iloscGotowki + aktualnyPunkt.getWzietyKredyt();
            System.out.println("$ Kredyt przyznano!");
            poczatkowyKredyt = aktualnyPunkt.getWzietyKredyt();
        }

        System.out.println("Dostepna gotowka \t" + gotowka);

        //rata = (kredyt / ilosc_rund_do_konca) + kredyt*0,12
        //
        rataKredytu = prognozujRateKredytu();
        ///

        /*
         * Wzór na obliczenie raty malejącej kredytu:

            część kapitałowa = poczatkowa kwota kredytu/ilość wszystkich rat
             część odsetkowa = kwota kredytu pozostała do spłaty * oprocentowanie w skali roku/ilość rat w roku
             rata =  część kapitałowa + część odsetkowa
         */
        //rataKredytu = poczatkowyKredyt/5 +
        //        (poczatkowyKredyt-aktualnyPunkt.getWzietyKredyt())* ProgramConst.oprocentowanieKredytu/5;



        if ( rataKredytu > aktualnyPunkt.getWzietyKredyt() ) {
            rataKredytu = aktualnyPunkt.getWzietyKredyt();
        }

        gotowka -= rataKredytu;
        System.out.println("Rata kredytu wynosi \t" + rataKredytu);
        System.out.println("Gotowka po splacie kredytu: \t" + gotowka);

        gotowka -= (aktualnyPunkt.getReklamaInet() + aktualnyPunkt.getReklamaMag() + aktualnyPunkt.getReklamaTV());
        System.out.println("Gotowka po oplaceniu reklam: \t" + gotowka);

        gotowka -= (ProgramConst.amortyzacja + ProgramConst.kosztyStale);
        System.out.println("Gotowka po kosztach stalych: \t" + gotowka);
    }

    public void zakonczEtap(){
        obliczDane();

        ++runda;
        System.out.println("### RUNDA NASTEPNA ### \nGotowka przed rozpoczeciem obliczen\t" + gotowka);

        System.out.println("Kredyt byl:" + aktualnyPunkt.getWzietyKredyt() + "\t splata to "+rataKredytu);
        double temp = aktualnyPunkt.getWzietyKredyt();
        if ( temp - rataKredytu > 0.1 ) {
            temp *= 1.12;
        }
        aktualnyPunkt.setWzietyKredyt( temp - rataKredytu );
        testKredyt += rataKredytu;
        System.out.println("Kredyt jest:" + aktualnyPunkt.getWzietyKredyt());
        double grosiki = gotowka * ProgramConst.oprocentowanieKontaBankowego;
        if ( grosiki > 0 ){
        gotowka += grosiki*0.81;
            System.out.println("Gotowka ++ %lokata: \t" + gotowka);
        }
        wolumen = aktualnyPunkt.getWolumen();

        System.out.println("Wolumen do kupna: " + wolumen + "\t za sztuke: " + aktualnyPunkt.getCenaJed());
        System.out.println("ZAROBEK: \t" + wolumen*aktualnyPunkt.getCenaJed()*0.81);
        gotowka += (wolumen*aktualnyPunkt.getCenaJed()*0.81);
        
        System.out.println("Gotowka ostatecznie: \t" + gotowka);
        System.out.println("Splacono kredytu: \t" + testKredyt);
    }

    public double getZyskFromCurrentPoint() {
        return wolumen*aktualnyPunkt.getCenaJedSprzedaz()*0.81;
    }


}//end class
